﻿#pragma once
#include <boost/asio.hpp>
#include <boost/thread.hpp>

#include <stdint.h>
#include <mutex>
#include <iostream>
#include "tcp_server.h"


#define HTONL(hostInt)  ((((uint32_t)(hostInt) & 0xff000000) >> 24) | \
						(((uint32_t)(hostInt) & 0x00ff0000) >> 8) | \
						(((uint32_t)(hostInt) & 0x0000ff00) << 8) | \
						(((uint32_t)(hostInt) & 0x000000ff) << 24))

using namespace boost::asio;
using namespace std;

#include <iostream>
#include <string>
#include <boost/bind/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
#include <boost/format.hpp>

#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

using boost::asio::ip::tcp;

std::string make_daytime_string()
{
	using namespace boost::posix_time;
	using namespace boost::gregorian;
	ptime _now_time = microsec_clock::local_time();
	return to_iso_extended_string(_now_time);
}

class tcp_connection
	: public boost::enable_shared_from_this<tcp_connection>
{
public:
	typedef boost::shared_ptr<tcp_connection> pointer;

	static pointer create(boost::asio::io_context& io_context)
	{
		return pointer(new tcp_connection(io_context));
	}

	tcp::socket& socket()
	{
		return socket_;
	}

	void start()
	{
		message_ = make_daytime_string();

		boost::asio::async_write(socket_, boost::asio::buffer(message_),
			boost::bind(&tcp_connection::handle_write, shared_from_this(),
				boost::asio::placeholders::error,
				boost::asio::placeholders::bytes_transferred));
	}

private:
	tcp_connection(boost::asio::io_context& io_context)
		: socket_(io_context)
	{
	}

	void handle_write(const boost::system::error_code& /*error*/,
		size_t /*bytes_transferred*/)
	{
	}

	tcp::socket socket_;
	std::string message_;
};

class tcp_server
{
public:
	tcp_server(boost::asio::io_context& io_context)
		: io_context_(io_context),
		acceptor_(io_context, tcp::endpoint(tcp::v4(), 13))
	{
		start_accept();
	}

private:
	void start_accept()
	{
		tcp_connection::pointer new_connection =
			tcp_connection::create(io_context_);

		acceptor_.async_accept(new_connection->socket(),
			boost::bind(&tcp_server::handle_accept, this, new_connection,
				boost::asio::placeholders::error));
	}

	void handle_accept(tcp_connection::pointer new_connection,
		const boost::system::error_code& error)
	{
		if (!error)
		{
			new_connection->start();
		}

		start_accept();
	}

	boost::asio::io_context& io_context_;
	tcp::acceptor acceptor_;
};


class TcpServer
{

	typedef TcpServer			this_type;
	typedef ip::tcp::acceptor	acceptor_type;
	typedef ip::tcp::endpoint	endpoint_type;
	typedef ip::tcp::socket		socket_type;
	typedef boost::shared_ptr<socket_type>	sock_ptr;
private:
	io_service		m_io;
	acceptor_type	m_acceptor;
	vector<char>	m_buf;
	vector<char>	m_sendbuf;
	mutex			mtx_src;

public:
	TcpServer(int point = 11002) :m_acceptor(m_io, endpoint_type(ip::tcp::v4(), point)), m_buf(1000, 0)
	{
		accept();
	}
	void run()
	{
		m_io.run();
	}
	void loadData(string send_str = "hello") {
		uint32_t headBigEndian = HTONL(0xAABBCCDD);
		uint32_t frameCount = HTONL(send_str.size());
		char _header[4];
		char _length[4];
		memcpy(_header, &headBigEndian, 4);
		memcpy(_length, &frameCount, 4);
		//加锁处理
		{
			unique_lock<mutex> lock(mtx_src);
			m_sendbuf.insert(m_sendbuf.end(), _header, _header + 4);
			m_sendbuf.insert(m_sendbuf.end(), _length, _length + 4);
			m_sendbuf.insert(m_sendbuf.end(), send_str.begin(), send_str.end());
		}
	}
	void accept()
	{
		sock_ptr sock(new socket_type(m_io));
		m_acceptor.async_accept(*sock, boost::bind(&this_type::accept_handler, this, boost::asio::placeholders::error, sock));
	}
	void accept_handler(const  boost::system::error_code& ec, sock_ptr sock)
	{
		if (ec) {
			//cout << ec << endl;
			return;
		}
		cout << "client:";
		cout << sock->remote_endpoint().address() << endl;

		sock->async_read_some(buffer(m_buf), boost::bind(&TcpServer::read_handler, this,
			boost::asio::placeholders::error,
			boost::asio::placeholders::bytes_transferred,
			sock));
		vector<char> a;
		sock->async_write_some(buffer(a), boost::bind(&this_type::write_handler, this,
			boost::asio::placeholders::error,
			boost::asio::placeholders::bytes_transferred,
			sock));
		cout << "dis connect" << endl;
		accept();
	}


	void write_handler(const  boost::system::error_code& ec, std::size_t n, sock_ptr sock) {
		if (ec)
			return;
		cout << "sent mst complete." << n << endl;
		if (n > 0)
		{
			unique_lock<mutex> lock(mtx_src);
			m_sendbuf.erase(m_sendbuf.begin(), m_sendbuf.begin() + n);
		}

		while (m_sendbuf.empty()) {};
		unique_lock<mutex> lock(mtx_src);
		sock->async_write_some(buffer(m_sendbuf), boost::bind(&this_type::write_handler, this,
			boost::asio::placeholders::error,
			boost::asio::placeholders::bytes_transferred,
			sock));
	}
	void read_handler(const boost::system::error_code& ec, std::size_t n, sock_ptr sock) {
		if (ec)
			return;
		cout << "receive " << n << "byte" << endl;
		cout << &m_buf[0] << endl;
		sock->async_read_some(buffer(m_buf), boost::bind(&TcpServer::read_handler, this,
			boost::asio::placeholders::error,
			boost::asio::placeholders::bytes_transferred,
			sock
		));
	}
};

TcpServer tcp_server(11002);
TcpSend::TcpSend(int point)
{
	std::thread server_thread([&] {
		tcp_server.run();
	});
	server_thread.detach();
}
void TcpSend::loadData(string send_str)
{
	tcp_server.loadData(send_str);
}



	//json::value jv = {
	//{ "pi", 3.141 },
	//{ "happy", true },
	//{ "name", "Boost" },
	//{ "nothing", nullptr },
	//{ "answer", {
	//	{ "everything", 42 } } },
	//{"list", {1, 0, 2}},
	//{"object", {
	//	{ "currency", "USD" },
	//	{ "value", 42.99 }
	//		} }
	//};
	//std::string s = serialize( jv );

	//tcp_server.loadData(s);
